package org.caenv.interpreter.parser;

import java.io.IOException;
import java.util.Stack;

import org.caenv.interpreter.pojo.instructions.ExecutionPlan;
import org.caenv.interpreter.pojo.instructions.Instruction;
import org.caenv.interpreter.pojo.instructions.Scope;
import org.caenv.interpreter.pojo.instructions.State;
import org.caenv.interpreter.pojo.instructions.comparator.PopComparator;
import org.caenv.interpreter.pojo.instructions.comparator.PushComparator;
import org.caenv.interpreter.util.SourceUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

public class ParserXMLHandler extends DefaultHandler {

    private final InputSource[] states;
    private final ExecutionPlan executionPlan;
    private final Stack<State> stateBuild;

    public ParserXMLHandler(ExecutionPlan executionPlan, InputSource... states) {
        this.executionPlan = executionPlan;
        this.states = states;
        this.stateBuild = new Stack<State>();
    }

    public void parse() throws SAXException, IOException {
        XMLReader xr = XMLReaderFactory.createXMLReader();
        xr.setContentHandler(this);
        xr.setErrorHandler(this);

        for (InputSource state : states) {
            xr.parse(state);
        }
    }

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    @Override
    public void endDocument() throws SAXException {
        executionPlan.addState(stateBuild.pop());
        super.endDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

        qName = qName.toLowerCase();

        /*
         * States
         */
        if ("state".equals(qName)) {
            stateBuild.push(new State(attributes.getValue("name")));
        }

        /*
         * Comparators
         */
        else if ("compare".equals(qName)) {
            this.stateBuild.peek().addInstruction(SourceUtils.buildInstruction(new PushComparator(), attributes));
        }

        /*
         * Other instructions
         */
        else {
            Instruction instruction = SourceUtils.buildInstruction(qName, attributes);

            if (instruction instanceof Scope) {
                this.stateBuild.push(new State("_compare"));
            } else {
                this.stateBuild.peek().addInstruction(instruction);
            }
        }

        super.startElement(uri, localName, qName, attributes);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {

        qName = qName.toLowerCase();

        /*
         * States
         */
        if ("state".equals(qName)) {
            return;
        }
               
        /*
         * Comparators
         */
        else if ("compare".equals(qName)) {
            this.stateBuild.peek().addInstruction(SourceUtils.buildInstruction(new PopComparator(), null));
        } 
        
        /*
         * Escopes
         */
        else if (SourceUtils.isEscope(qName)) {
            State block = this.stateBuild.pop();
            Scope scope = (Scope) SourceUtils.buildInstruction(qName, null);
            scope.setState(block);
            this.stateBuild.peek().addInstruction(scope);
        }

        super.endElement(uri, localName, qName);
    }

    public ExecutionPlan getExecution() {
        return executionPlan;
    }
}
